package com.example.service.impl;

import com.example.dto.CouponDiscountDTO;
import com.example.dto.DiscountDetailDTO;
import com.example.dto.OrderItemDTO;
import com.example.entity.UserCoupon;
import com.example.service.DiscountService;
import com.example.service.UserCouponService;
import com.example.strategy.Discount;
import com.example.strategy.DiscountStrategy;
import com.example.util.CollUtils;
import com.example.util.PermuteUtil;
import com.example.util.RedisUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author circle48
 */
@Service
@Slf4j
public class DiscountServiceImpl implements DiscountService {

    @Resource
    private RedisUtil redisUtil;


    @Resource
    private UserCouponService userCouponService;

    @Resource
    private Executor discountSolutionExecutor;



//    /**
//     * 获取可使用的优惠券列表
//     *
//     * @return 可使用的优惠券列表
//     */
//    @Override
//    public List<UserCoupon> getCanUseCoupons() {
//        TradeDTO tradeDTO = this.readDTO();
//        List<UserCoupon> canUseCoupons = new ArrayList<>();
//        // 计算交易中所有商品的总价
//        double totalPrice = tradeDTO.getSkuList().stream()
//                .mapToDouble(i -> i.getPurchasePrice() * i.getNum())
//                .sum();
//
//        if (!tradeDTO.getSkuList().isEmpty()) {
//            // 获取商品ID
//            List<Long> ids = tradeDTO.getSkuList().stream()
//                    .filter(i -> Boolean.TRUE.equals(i.getChecked()))
//                    .map(i -> i.getGoodsSku().getId())
//                    .collect(Collectors.toList());
//
//            // 获取商品分类ID
//            List<Long> categoryIds = tradeDTO.getSkuList().stream()
//                    .map(i -> i.getGoodsSku().getCategoryId())
//                    .distinct()
//                    .collect(Collectors.toList());
//
//            // 获取店铺ID
//            List<Long> storeIds = tradeDTO.getSkuList().stream()
//                    .map(CartSkuVO::getStoreId)
//                    .distinct()
//                    .collect(Collectors.toList());
//
//            List<Sku> GoodsList = skuService.getGoodsBySkuIds(ids);
//            for (Sku GoodsIndex : GoodsList) {
//                if (GoodsIndex.getPromotionMap() != null && !GoodsIndex.getPromotionMap().isEmpty()) {
//                    List<Long> couponIds = GoodsIndex.getPromotionMap().keySet().stream()
//                            .filter(i -> i.contains("优惠券"))
//                            .map(i -> i.substring(i.lastIndexOf("-") + 1))
//                            .map(Long::parseLong)
//                            .collect(Collectors.toList());
//
//                    if (!couponIds.isEmpty()) {
//                        List<UserCoupon> currentGoodsCanUse = getCurrentGoodsCanUse(tradeDTO.getMemberId(), couponIds, totalPrice, GoodsIndex.getCategoryId(), GoodsIndex.getStoreId());
//                        canUseCoupons.addAll(currentGoodsCanUse);
//                    }
//                }
//            }
//
//            List<UserCoupon> allScopeMemberCoupon = getAllScopeUserCoupon(tradeDTO.getMemberId(), storeIds);
//            if (allScopeMemberCoupon != null && !allScopeMemberCoupon.isEmpty()) {
//                canUseCoupons.addAll(allScopeMemberCoupon.stream()
//                        .filter(i -> i.getMinPoint() <= totalPrice)
//                        .collect(Collectors.toList()));
//            }
//        }
//        return canUseCoupons;
//    }
//
//    /**
//     * 获取当前会员全品类优惠券
//     *
//     * @param userId 会员Id
//     * @param storeId  店铺id
//     * @return 会员优惠券列表
//     */
//    @Override
//    public List<UserCoupon> getAllScopeUserCoupon(Long userId, List<Long> storeId) {
//        LambdaQueryWrapper<UserCoupon> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(UserCoupon::getUserId, userId);
//        queryWrapper.eq(UserCoupon::getStatus, UserCouponStatusEnum.UNUSED.getValue());
//        queryWrapper.eq(UserCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name());
//        queryWrapper.ge(UserCoupon::getEndTime, new Date()).and(i -> i.in(UserCoupon::getStoreId, storeId).or(j -> j.eq(UserCoupon::getPlatformFlag, true)));
//        return userCouponMapper.list(queryWrapper);
//    }
//    /**
//     * 获取当前会员当前商品可用的会员优惠券
//     *
//     * @param userId  会员Id
//     * @param couponIds 优惠券id列表
//     * @return 会员优惠券列表
//     */
//
//    @Override
//    public List<UserCoupon> getCurrentGoodsCanUse(Long userId, List<Long> couponIds, Double totalPrice, Long categoryId, Long storeId) {
//        LambdaQueryWrapper<UserCoupon> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(UserCoupon::getUserId, userId);
//        queryWrapper.eq(UserCoupon::getStatus, UserCouponStatusEnum.UNUSED.getValue());
//        queryWrapper.in(UserCoupon::getCouponId, couponIds);
//        queryWrapper.le(UserCoupon::getMinPoint, totalPrice);
//        queryWrapper.ge(UserCoupon::getEndTime, new Date());
//        queryWrapper.and(wrapper -> wrapper.eq(UserCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name())
//                .or().eq(UserCoupon::getScopeType, PromotionsScopeTypeEnum. PORTION_GOODS.name()).eq(UserCoupon::getScopeId, storeId)
//                .or().eq(UserCoupon::getScopeType, PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()).eq(UserCoupon::getScopeId, categoryId)
//                .or().eq(UserCoupon::getScopeType, PromotionsScopeTypeEnum.PORTION_SHOP_CATEGORY.name()).eq(UserCoupon::getScopeId, storeId));
//        return this.list(queryWrapper);
//    }
//
//    public TradeDTO readDTO() {
//        //获取用户信息
//        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
//        Long id = loginUser.getUser().getId();
//        String cacheKey = "tradeDTO:" + id;
//
//        // 尝试从Redis获取TradeDTO
//        TradeDTO tradeDTO = (TradeDTO) redisUtil.get(cacheKey);
//
//        if (tradeDTO == null) {
//            // 获取用户信息
//            tradeDTO = new TradeDTO(); // 创建新的TradeDTO对象
//            tradeDTO.setMemberId(id);
//            tradeDTO.setMemberName(loginUser.getUser().getUsername());
//
//            // 将新的TradeDTO对象保存到Redis
//            redisUtil.set(cacheKey, tradeDTO);
//        }
//
//        if (tradeDTO.getMemberAddress() == null) {
//            tradeDTO.setMemberAddress(this.userAddressService.getDefaultMemberAddress());
//        }
//
//        return tradeDTO;
//    }
    /**
     * 查找优惠解决方案
     *
     * @param orderList 订单项数据列表，包含用户购买的商品信息
     * @return 返回一个优惠券折扣信息列表，其中包含了多种可能的优惠方案及其折扣详情
     */
    @Override
    public List<CouponDiscountDTO> findDiscountSolution(List<OrderItemDTO> orderList) {
        // 查询用户可用的优惠券列表
        List<UserCoupon> couponList = userCouponService.queryMyCoupon();
        if (CollUtils.isEmpty(couponList)) {
            return CollUtils.emptyList();
        }

//        // 根据优惠券的类型进行分组，并对分组后的结果进行排序
//        Map<PromotionsScopeTypeEnum, List<UserCoupon>> couponMap = couponList.stream()
//                .collect(Collectors.groupingBy(UserCoupon::getScopeType));
//
//        // 优先级排序
//        List<PromotionsScopeTypeEnum> prioritizedTypes = Arrays.asList(
//                PromotionsScopeTypeEnum.PORTION_GOODS,
//                PromotionsScopeTypeEnum.PORTION_SHOP_CATEGORY,
//                PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY,
//                PromotionsScopeTypeEnum.ALL
//        );
//        // 根据优先级顺序筛选出高优先级的优惠券
//        List<UserCoupon> prioritizedCoupons = new ArrayList<>();
//        for (PromotionsScopeTypeEnum type : prioritizedTypes) {
//            if (couponMap.containsKey(type)) {
//                prioritizedCoupons.addAll(couponMap.get(type));
//            }
//        }

        // 细筛（筛选出优惠券可用的商品）
        Map<UserCoupon, List<OrderItemDTO>> availableCouponMap = findAvailableCouponMap(couponList, orderList);
        if (CollUtils.isEmpty(availableCouponMap)) {
            return CollUtils.emptyList();
        }
        // 生成可用优惠券的列表
        List<UserCoupon> availableCoupons = new ArrayList<>(availableCouponMap.keySet());
        // 完成全排列
        List<List<UserCoupon>> solutions = PermuteUtil.permute(availableCoupons);
        for (UserCoupon coupon : couponList) {
            solutions.add(List.of(coupon));
        }

        // 并行计算每种方案
        List<CouponDiscountDTO> dtoList = Collections.synchronizedList(new ArrayList<>());
        //保证所有线程计算完毕再去计算最优方案，定义闭锁
        CountDownLatch latch = new CountDownLatch(solutions.size());
        for (List<UserCoupon> solution : solutions) {
            //异步计算
            CompletableFuture.supplyAsync(() -> calculateSolution(solution, availableCouponMap, orderList), discountSolutionExecutor)
                    .thenAccept(dto -> {
                        dtoList.add(dto);
                        latch.countDown();
                    }).exceptionally(e -> {
                        log.error("CompletableFuture执行异常: {}", e.getMessage());
                        latch.countDown();
                        return null;
                    });
        }
        try {
            latch.await(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            log.error("计算方案超时:{}", e.getMessage());
        }

        // 筛选最优解
        return findBestSolution(dtoList);
    }


    /**
     * 查找可用优惠券及其适用的商品列表，并将结果映射起来。
     *
     * @param availableCouponList 可用的优惠券列表
     * @param orderList 订单项列表，即用户选购的商品列表
     * @return 返回一个映射，键为可用的优惠券，值为该优惠券适用的商品列表。如果某个优惠券适用于多个商品，则这些商品会聚合在同一个键下。
     */
    private Map<UserCoupon, List<OrderItemDTO>> findAvailableCouponMap(List<UserCoupon> availableCouponList, List<OrderItemDTO> orderList) {
        Map<UserCoupon, List<OrderItemDTO>> availableCouponMap = new HashMap<>();
        // 找到当前优惠券适用的商品列表
        for (UserCoupon coupon : availableCouponList) {
            // 找到当前优惠券适用的商品列表
            List<OrderItemDTO> availableItemList = findApplicableItems(coupon, orderList);
            // 检查商品总价是否满足优惠券门槛
            BigDecimal totalAmount = calculateTotalAmount(availableItemList);
            if (DiscountStrategy.getDiscount(coupon.getCouponCategory()).canUse(totalAmount, coupon)) {
                availableCouponMap.put(coupon, availableItemList);
            }
        }
        return availableCouponMap;
    }

    /**
     * 根据优惠券的适用范围找到适用的商品项。
     *
     * @param coupon 用户优惠券，用于确定优惠券的适用范围。
     * @param orderList 订单项列表，包含所有待处理的商品项。
     * @return 返回一个列表，包含根据优惠券的适用范围筛选出的适用商品项。
     */
    private List<OrderItemDTO> findApplicableItems(UserCoupon coupon, List<OrderItemDTO> orderList) {
        // 根据优惠券的适用范围类型进行不同的筛选
        switch (coupon.getScopeType()) {
            case PORTION_GOODS_CATEGORY: // 按商品类别适用
                return filterByCategory(coupon.getScopeId(), orderList);
            case PORTION_GOODS:// 按具体商品适用
                return filterByGoods(coupon.getScopeId(), orderList);
            case PORTION_SHOP_CATEGORY: // 按店铺类别适用
                return filterByStore(coupon.getScopeId(), orderList);
            default: // 默认情况下，不适用任何筛选条件
                return orderList;
        }
    }

    /**
     * 根据指定的商品分类过滤订单项列表。
     *
     * @param categoryId 指定的商品分类ID。
     * @param orderList 待过滤的订单项列表。
     * @return 符合指定商品分类的订单项列表。
     */
    private List<OrderItemDTO> filterByCategory(Long categoryId, List<OrderItemDTO> orderList) {
        return orderList.stream()
                .filter(item -> item.getCategoryId().equals(categoryId))
                .collect(Collectors.toList());
    }

    /**
     * 过滤出符合指定商品的订单项
     *
     * @param goodsId 指定的商品ID，用于筛选订单项
     * @param orderList 待筛选的订单项列表
     * @return 返回一个包含所有符合指定商品ID的订单项的列表
     */
    private List<OrderItemDTO> filterByGoods(Long goodsId, List<OrderItemDTO> orderList) {
        return orderList.stream()
                .filter(item -> item.getSpuId().equals(goodsId))
                .collect(Collectors.toList());
    }

    /**
     * 过滤出符合指定店铺的订单项
     *
     * @param storeId 指定店铺的ID，用于筛选订单项
     * @param orderList 待筛选的订单项列表
     * @return 返回符合指定店铺ID的订单项列表
     */
    private List<OrderItemDTO> filterByStore(Long storeId, List<OrderItemDTO> orderList) {
        return orderList.stream()
                .filter(item -> item.getStoreId().equals(storeId))
                .collect(Collectors.toList());
    }

    /**
     * 计算订单总金额
     * @param orderList 订单项数据列表，包含每个订单项的价格信息
     * @return 返回订单列表的总金额，以BigDecimal类型表示，确保精确度
     */
    private BigDecimal calculateTotalAmount(List<OrderItemDTO> orderList) {
        // 使用流处理订单列表，将每个订单项的价格与数量相乘，然后相加得出总金额
        return orderList.stream()
                .map(item -> item.getSkuPrice().multiply(new BigDecimal(item.getSkuQuantity()))) // 映射操作，提取每个订单项的价格并乘以数量
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 累加操作，将所有订单项的总价相加
    }

    /**
     * 计算优惠券方案
     * @param solution 优惠券方案，即优惠券列表
     * @param availableCouponMap 可用的优惠券及其适用的商品列表
     * @param orderList 订单项列表，包含每个订单项的价格信息
     * @return CouponDiscountDTO 返回一个优惠券方案对象，其中包含了优惠券方案信息、每个订单项的优惠金额、优惠券规则等。
     */
    private CouponDiscountDTO calculateSolution(List<UserCoupon> solution, Map<UserCoupon, List<OrderItemDTO>> availableCouponMap, List<OrderItemDTO> orderList) {
        //存储优惠券方案信息
        CouponDiscountDTO dto = new CouponDiscountDTO();
        //记录每个商品的优惠详情
        Map<Long, BigDecimal> detailMap = new HashMap<>();
        //用于记录每个商品使用的优惠券
        Map<Long, List<Long>> itemCouponMap = new HashMap<>();
        // 遍历用户选择的每个优惠券
        for (UserCoupon coupon : solution) {
            // 如果优惠券不可用，则跳过
            if (!availableCouponMap.containsKey(coupon)) {
                continue;
            }

            List<OrderItemDTO> orderItemList = availableCouponMap.get(coupon);
            BigDecimal totalAmount = calculateTotalAmount(orderItemList);
            Discount discount = DiscountStrategy.getDiscount(coupon.getCouponCategory());
            // 如果当前优惠券不满足使用条件，则跳过
            if (!discount.canUse(totalAmount, coupon)) {
                continue;
            }

            BigDecimal discountAmount = discount.calculateDiscount(totalAmount, coupon);
            List<DiscountDetailDTO> discountDetails = new ArrayList<>();
            // 计算每个商品项的优惠详情，并记录使用的优惠券
            calculateDiscountDetail(detailMap, totalAmount, discountAmount, orderItemList, discountDetails, coupon, itemCouponMap);
            dto.setId(UUID.randomUUID().hashCode());
            dto.getIds().add(coupon.getId());
            // 添加优惠券规则描述
            dto.getRules().add(discount.getRule(coupon));
            dto.setDiscountAmount(dto.getDiscountAmount().add(discountAmount));
        }
        // 生成最终的优惠详情
        dto.setDiscountDetails(generateDiscountDetails(detailMap, orderList, itemCouponMap));
        return dto;
    }

    /**
     * 计算商品折扣详情。
     *
     * @param detailMap 用于存储每个订单项的折扣详情的映射，键为订单项ID，值为折扣金额。
     * @param totalAmount 商品总金额。
     * @param discountAmount 折扣总金额。
     * @param orderItemList 订单项列表。
     * @param discountDetails 折扣详情列表，用于存储计算后的折扣信息。
     * @param coupon 当前优惠券
     * @param itemCouponMap 用于记录每个订单项使用的优惠券
     */
    private void calculateDiscountDetail(Map<Long, BigDecimal> detailMap, BigDecimal totalAmount, BigDecimal discountAmount,
                                         List<OrderItemDTO> orderItemList, List<DiscountDetailDTO> discountDetails,
                                         UserCoupon coupon, Map<Long, List<Long>> itemCouponMap) {
        if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
            // 检查总价是否合理，如果不合理则无需计算折扣
            return;
        }

        BigDecimal remainingDiscount = discountAmount;

        // 遍历订单项列表
        for (int i = 0; i < orderItemList.size(); i++) {
            OrderItemDTO order = orderItemList.get(i);
            if (order == null || order.getSkuPrice() == null) {
                continue; // 如果订单项或价格无效，则跳过
            }

            // 计算当前订单项的应付金额
            BigDecimal itemTotalAmount = order.getSkuPrice().subtract(detailMap.getOrDefault(order.getId(), BigDecimal.ZERO));
            if (itemTotalAmount.compareTo(BigDecimal.ZERO) <= 0) {
                continue; // 如果计算后的商品总价不合理，则跳过
            }

            BigDecimal discountPerItem = BigDecimal.ZERO;

            // 计算当前商品的折扣金额
            if (remainingDiscount.compareTo(BigDecimal.ZERO) > 0) {
                // 如果是最后一个订单项，则将剩余折扣全部应用于该订单
                if (i == orderItemList.size() - 1) {
                    discountPerItem = remainingDiscount;
                } else {
                    discountPerItem = itemTotalAmount.multiply(discountAmount).divide(totalAmount, RoundingMode.DOWN);
                    discountPerItem = discountPerItem.min(itemTotalAmount).min(remainingDiscount);
                    remainingDiscount = remainingDiscount.subtract(discountPerItem);
                }
            }

            BigDecimal originalPrice = order.getSkuPrice();
            BigDecimal finalPrice = originalPrice.subtract(discountPerItem);

            // 更新订单项的折扣详情
            detailMap.put(order.getId(), detailMap.getOrDefault(order.getId(), BigDecimal.ZERO).add(discountPerItem));

            // 记录该订单项使用的优惠券
            itemCouponMap.computeIfAbsent(order.getId(), k -> new ArrayList<>()).add(coupon.getId());

            // 创建并填充折扣详情对象
            DiscountDetailDTO detailDTO = new DiscountDetailDTO();
            detailDTO.setItemId(order.getId());
            detailDTO.setOriginalPrice(originalPrice);
            detailDTO.setDiscountAmount(discountPerItem);
            detailDTO.setFinalPrice(finalPrice);
            discountDetails.add(detailDTO);
        }
    }

    /**
     * 生成折扣详情列表。
     *
     * @param detailMap 包含商品ID与对应折扣金额的映射表，如果商品ID不存在则默认折扣为0。
     * @param orderList 订单项列表，包含了需要计算折扣的各个商品信息。
     * @param itemCouponMap 包含每个订单项使用的优惠券ID列表的映射
     * @return 返回一个折扣详情数据传输对象（DTO）的列表，每个对象包含商品ID、原价、折扣金额和最终价格。
     */
    private List<DiscountDetailDTO> generateDiscountDetails(Map<Long, BigDecimal> detailMap, List<OrderItemDTO> orderList, Map<Long, List<Long>> itemCouponMap) {
        List<DiscountDetailDTO> discountDetails = new ArrayList<>();
        for (OrderItemDTO item : orderList) {
            BigDecimal discount = detailMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            DiscountDetailDTO detailDTO = new DiscountDetailDTO();
            detailDTO.setItemId(item.getSkuId());
            detailDTO.setOriginalPrice(item.getSkuPrice());
            detailDTO.setDiscountAmount(discount);
            detailDTO.setFinalPrice(
                    item.getSkuPrice().multiply(new BigDecimal(item.getSkuQuantity())).subtract(discount)
            );
            detailDTO.setQuantity(item.getSkuQuantity()); // 设置商品数量

            // 设置使用的优惠券ID
            detailDTO.setUsedCouponIds(itemCouponMap.getOrDefault(item.getId(), new ArrayList<>()));

            discountDetails.add(detailDTO);
        }
        return discountDetails;
    }


    /**
     * 查找最优的优惠方案。
     *
     * @param dtoList 优惠方案列表，每个方案包含优惠券ID集合及优惠金额。
     * @return 返回最优的优惠方案列表，该列表中的方案按照优惠金额降序排列。
     */
    private List<CouponDiscountDTO> findBestSolution(List<CouponDiscountDTO> dtoList) {
        // 1.定义两个map，分别用于存储优惠金额更高和用券更少的优惠方案
        Map<String, CouponDiscountDTO> moreDiscountMap = new HashMap<>();
        Map<BigDecimal, CouponDiscountDTO> lessCouponMap = new HashMap<>();
        // 2.遍历所有优惠方案
        for (CouponDiscountDTO solution : dtoList) {
            //3. 计算优惠方案的id组合 1,3,2 =sorted=>1,2,3=map=>"1" "2" "3"=collect=>"1,2,3"
            String ids = solution.getIds().stream().sorted(Long::compareTo)
                    .map(String::valueOf).collect(Collectors.joining(","));
            //.map(id -> String.valueOf(id)).collect(Collectors.joining(","));
            //4. 比较用券相同时,优惠金额是否最高
            CouponDiscountDTO best = moreDiscountMap.get(ids);
            //如果方案的折扣金额比当前的solution折扣金额还大, continue
            if (best != null && best.getDiscountAmount().compareTo(solution.getDiscountAmount()) >= 0) {
                continue;
            }

            //5. 比较优惠金额相同时,用券是否最少
            best = lessCouponMap.get(solution.getDiscountAmount());
            //方案的折扣金额相同, 如果方案的使用优惠券的数量比当前的solution的优惠券的数量还少, continue; solution.getIds().size()>1: 保留单券
            if (best != null && best.getIds().size() <= solution.getIds().size() && solution.getIds().size() > 1) {
                continue;
            }
            //6. 更新map
            moreDiscountMap.put(ids, solution);
            lessCouponMap.put(solution.getDiscountAmount(), solution);
        }

        //7. 求两个map的交集,并且按照discountAmount降序(reversed())
        Collection<CouponDiscountDTO> intersection = CollUtils.intersection(moreDiscountMap.values(), lessCouponMap.values());
        return intersection.stream()
                .sorted((dto1, dto2) -> dto2.getDiscountAmount().compareTo(dto1.getDiscountAmount()))
                .collect(Collectors.toList());
    }




}
